package com.bawei.es.service.impl;

import com.bawei.common.core.domain.R;
import com.bawei.common.core.exception.ServiceException;
import com.bawei.common.core.utils.StringUtils;
import com.bawei.system.domain.vo.EsDocDelVo;
import com.bawei.system.domain.vo.IndexDetailsVO;
import com.bawei.es.service.EsIndexService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.rest.RestStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

@Service
@Slf4j
public class EsIndexServiceImpl implements EsIndexService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public R<Boolean> indexCreate(String indexName) {
        // 如果索引为空 抛出异常
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("索引名称不能为空");
        }

        CreateIndexRequest request = new CreateIndexRequest(indexName);
        try {
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            if (createIndexResponse.isAcknowledged()) {
                return R.ok(true); // 假设R.ok是一个通用的响应构造器
            } else {
                // 通常情况下，如果请求被接受但尚未被确认，这里不会返回，但你可以根据需要进行处理
                // 可以选择记录日志或返回一个不同的响应
                log.warn("索引创建请求已发送但尚未被确认: {}", indexName);
                throw new RuntimeException("索引创建请求已发送但尚未被确认");
            }
        } catch (IOException e) {
            Throwable cause = e.getCause();
            if (cause instanceof ElasticsearchException) {
                ElasticsearchException esException = (ElasticsearchException) cause;
                if (esException.status() == RestStatus.BAD_REQUEST &&
                        esException.getMessage().contains("resource_already_exists_exception")) {
                    // 如果异常是400 BAD_REQUEST且消息包含 "resource_already_exists_exception"，则索引已存在
                    throw new RuntimeException(indexName+"索引已经存在"); // 索引已存在，返回特定响应
                }
            }
            // 其他类型的Elasticsearch异常或IO异常
            log.error("连接ES服务异常:{}", e);
            throw new RuntimeException("创建索引失败，请联系管理员或查看服务日志");
        }
    }

    @Override
    public R<Boolean> indexExit(String indexName) {
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空");
        }
        GetIndexRequest indexRequest = new GetIndexRequest(indexName);
        try {
            boolean exists = restHighLevelClient.indices().exists(indexRequest, RequestOptions.DEFAULT);
            return R.ok(exists);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.fail(false, "查询失败！");
    }

    @Override
    public R<Boolean> indexDelete(String indexName) {
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空 ");
        }
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        try {
            AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
            if (delete.isAcknowledged()) {
                return R.ok(true, "删除成功！");
            }
        } catch (IOException e) {
            log.error("删除索引失败：{}", e);
            throw new RuntimeException("调用es服务删除索引失败，详情请查看日志");
        }
        return R.fail(false, "删除索引失败");
    }

    @Override
    public R<Boolean> indexCreateWithMapping(String indexName, String indexMapping) {
        if (StringUtils.isEmpty(indexName) ||
                StringUtils.isEmpty(indexMapping)) {
            throw new ServiceException("indexName、indexMapping不能为空");
        }
        CreateIndexRequest request =  new CreateIndexRequest(indexName);
        request.source(indexMapping, XContentType.JSON);
        try {
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            if (createIndexResponse.isAcknowledged()){
                return R.ok(true,"创建索引成功");
            }
        } catch (IOException e) {
            log.error("调用es创建索引异常：{}",e);
            throw new RuntimeException("创建索引失败");
        }

        return R.fail(false,"创建索引失败");
    }

    @Override
    public R<Map> indexGetMapping(String indexName) {
        if (StringUtils.isEmpty(indexName)){
            throw new ServiceException("indexName不能为空");
        }
        GetMappingsRequest request = new GetMappingsRequest().indices(indexName);
        try {
            GetMappingsResponse response = restHighLevelClient.indices().getMapping(request, RequestOptions.DEFAULT);
            Map<String, MappingMetaData> mappings = response.mappings();
            MappingMetaData mappingMetaData = mappings.get(indexName);
            Map<String, Object> stringObjectMap = mappingMetaData.sourceAsMap();
            return R.ok(stringObjectMap,"操作成功");
        } catch (IOException e) {
            log.error("根据索引名称获取Mapping异常:{}",e);
            throw new RuntimeException("根据索引名称获取Mapping异常");
        }
    }

    /**
     * 添加一个修改索引接口，要可以修改已有的mappings映射，比如增加一个字段、删除一个字段等
     * @param indexName
     * @param mappings
     * @return
     */
    @Override
    public R updateIndex(String indexName, Map<String, Object> mappings) {
        try {
            //根据索引名称查找文档id进行修改
            String indexId= (String) mappings.get(indexName);
            // 准备UpdateRequest
            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject();
            {
                builder.field(mappings.toString()); // 要更新的字段和值
                // 可以继续添加其他字段...
            }
            builder.endObject();
            UpdateRequest updateRequest = new UpdateRequest() // 索引名称
                    .id(String.valueOf(indexId)) // 文档ID
                    .doc(builder);// 使用XContentBuilder构建的文档
            // 发送更新请求到ES
            UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
            // 检查响应状态
            RestStatus status = updateResponse.status();
            if (status == RestStatus.OK || status == RestStatus.CREATED ) {
                // 更新成功
                return R.ok("更新成功");
            } else {
                // 更新失败，处理错误
                throw new RuntimeException("更新失败");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据索引名称、映射结构创建索引
     *在现有的创建索引的接口要加上settings
     * @param vo
     * @return
     */
    @Override
    public R<Boolean> indexCreat(IndexDetailsVO vo) {
        if (StringUtils.isEmpty(vo.getIndexName()) ||
                vo.getMappings() == null ||
                vo.getSettings() == null) {
            throw new ServiceException("indexName、indexMapping、indexSetting不能为空");
        }

        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 将indexMappings和indexSettings转换为JSON字符串
            String indexMappingsJson = objectMapper.writeValueAsString(vo.getMappings());
            String indexSettingsJson = objectMapper.writeValueAsString(vo.getSettings());

            CreateIndexRequest request = new CreateIndexRequest(vo.getIndexName());
            request.source(indexMappingsJson, XContentType.JSON);

            // 设置索引的settings
            if (StringUtils.isNotEmpty(indexSettingsJson)) {
                request.settings(indexSettingsJson, XContentType.JSON);
            }

            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            if (createIndexResponse.isAcknowledged()) {
                return R.ok(true, "创建索引成功");
            }
        } catch (IOException e) {
            log.error("调用ES创建索引异常：{}", e);
            throw new RuntimeException("创建索引失败", e);
        }
        return R.fail(false, "创建索引失败");
    }

    @Override
    public R<Map<String, Object>> getMapping(String indexName) {
        R<Map<String, Object>> result = new R<>();
        Map<String, Object> dataMap = new HashMap<>();

        // 获取映射信息
        GetMappingsResponse response = null;
        try {
            response = restHighLevelClient.indices().getMapping(new GetMappingsRequest().indices(indexName), RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        MappingMetaData mappingMetaData = response.mappings().get(indexName);
        Map<String, Object> mappingSourceMap = mappingMetaData.sourceAsMap();

        // 获取设置
        GetSettingsRequest settingsRequest = new GetSettingsRequest().indices(indexName);
        GetSettingsResponse settingsResponse = null;
        try {
            settingsResponse = restHighLevelClient.indices().getSettings(settingsRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        ImmutableOpenMap<String, Settings> indexToSettings = settingsResponse.getIndexToSettings();
        Settings indexSettings = indexToSettings.get(indexName);
        // 将Settings转换为Map<String, String>
        Map<String, String> settingsMap = new HashMap<>();
        for (String key : indexSettings.keySet()) {
            settingsMap.put(key, indexSettings.get(key));
        }
        // 将映射信息和设置加入到结果中
        dataMap.put("settings", settingsMap);
        dataMap.put("mappings", mappingSourceMap);

        result.setData(dataMap);
        return result;
    }

    /**
     * 功能 根据索引名称获取mapping setting
     * @param indexName
     * @return
     */
    @Override
    public IndexDetailsVO get(String indexName) {
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空");
        }
        GetMappingsRequest mappingsRequest = new GetMappingsRequest().indices(indexName);
        GetSettingsRequest settingsRequest = new GetSettingsRequest().indices(indexName);
        IndexDetailsVO index = new IndexDetailsVO();
        try {
            // 获取mapping
            GetMappingsResponse mappingsResponse = restHighLevelClient.indices().getMapping(mappingsRequest, RequestOptions.DEFAULT);
            Map<String, MappingMetaData> mappings = mappingsResponse.mappings();
            MappingMetaData mappingMetaData = mappings.get(indexName);
            Map<String, Object> MappingsMap = mappingMetaData.sourceAsMap();
            // 获取setting
            GetSettingsResponse settingsResponse = restHighLevelClient.indices().getSettings(settingsRequest, RequestOptions.DEFAULT);
            Settings settings = settingsResponse.getIndexToSettings().get(indexName);
            HashMap<String, Object> settingsMap = new HashMap<>();
            if (settings != null){
                settings.keySet().forEach(settingKey -> {
                    String settingValue = settings.get(settingKey);
                    settingsMap.put(settingKey, settingValue);
                });
            }
            index.setIndexName(indexName);
            index.setMappings(MappingsMap);
            index.setSettings(settingsMap);
            return index;
        } catch (Exception e) {
            log.error("根据索引名称获取索引信息异常:{}", e);
            throw new RuntimeException("根据索引名称获取Mapping异常");
        }
    }

    @Override
    public void update(IndexDetailsVO vo, Integer flag) {
        if (StringUtils.isEmpty(vo.getIndexName())){
            throw new ServiceException("indexName不能为空");
        }
        if (vo.getMappings().isEmpty() && vo.getSettings().isEmpty()) {
            throw new RuntimeException("请至少更新映射或配置之一");
        }
        // 根据索引名称查询mapping映射
        IndexDetailsVO oldIndex = get(vo.getIndexName());
        // 从传入的参数中取出 mapping 的 properties
        Map requestProperties = (Map) vo.getMappings().get("properties");
        Map properties = new HashMap();
        if (requestProperties != null) {
            // 根据索引名取出 索引原本存在的 mapping 的 properties
            properties = (Map) oldIndex.getMappings().get("properties");
        }
        // 从传入的参数中取出 setting
        Map requestSettings = vo.getSettings();
        Map settings = new HashMap();
        if (requestSettings != null) {
            // 根据索引名取出 索引原本存在的 setting
            settings = oldIndex.getSettings();
        }
        // 传来一个标记，如果为 1 代表要删除字段映射 否则 代表修改或添加映射
        if (flag == 1) {
            // 获取请求发送的映射的所有字段名
            Set mappingsKey = requestProperties.keySet();
            // 从原本的映射中移除传入的字段名
            properties.keySet().removeAll(mappingsKey);

            // 获取请求发送的配置的所有配置名
            Set settingsKey = requestSettings.keySet();
            // 从原本的配置中移除传入的配置名
            settings.keySet().removeAll(settingsKey);
        }
        if (requestProperties != null) {
            // 重新给properties赋值
            vo.getMappings().put("properties", properties);
            // 将传入的properties字段名放入 根据索引名称查询到的 properties 中 (如果已有该字段，则会覆盖原先的值，以此来达到修改的效果)
            properties.putAll(requestProperties);
        }
        if (requestSettings != null) {
            // 给settings赋值
            vo.getSettings().putAll(requestSettings);
        }
        // es不支持直接删除映射，所以需要删除索引重新创建索引映射
        indexDelete(vo.getIndexName());
        try {
            // 重新创建索引
            createIndex(vo);
        } catch (Exception e) {
            // 报错后重新按原本的索引配置创建索引，避免到最后一步报错导致原本索引被删除无法恢复
            create(oldIndex);
            throw new RuntimeException(e);
        }

    }

    /**
     * 根据索引名称、映射结构创建索引
     * @param vo
     */
    @Override
    public void createIndex(IndexDetailsVO vo) {
        if (StringUtils.isEmpty(vo.getIndexName())) {
            throw new ServiceException("indexName不能为空");
        }
        // 判断索引是否已经存在,不存在则创建索引
        if (StringUtils.isEmpty(vo.getIndexName())) {
            CreateIndexRequest request = new CreateIndexRequest(vo.getIndexName());
            if (ObjectUtils.isNotEmpty(vo.getSettings())) {
                request.settings(vo.getSettings());
            }
            if (ObjectUtils.isNotEmpty(vo.getMappings())) {
                request.mapping(vo.getMappings());
            }
            try {
                restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            } catch (Exception e) {
                log.error("调用es创建索引异常：{}", e);
                throw new RuntimeException("创建索引失败");
            }
        }
    }

    @Override
    public void create(IndexDetailsVO vo) {
        if (StringUtils.isEmpty(vo.getIndexName())) {
            throw new ServiceException("indexName不能为空");
        }
        // 判断索引是否已经存在,不存在则创建索引
        if (StringUtils.isEmpty(vo.getIndexName())) {

            CreateIndexRequest request = new CreateIndexRequest(vo.getIndexName());
            if (ObjectUtils.isNotEmpty(vo.getSettings())) {
                request.settings(vo.getSettings());
            }
            if (ObjectUtils.isNotEmpty(vo.getMappings())) {
                request.mapping(vo.getMappings());
            }
            try {
                restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            } catch (Exception e) {
                log.error("调用es创建索引异常：{}", e);
                throw new RuntimeException("创建索引失败");
            }
        }
    }

    /**
     * 删除相对于的索引
     * @param esDocDelVo
     * @return
     */
    @Override
    public R indexDel(EsDocDelVo esDocDelVo) {
        if (esDocDelVo==null) {
            throw new ServiceException("esDocDelVo不能为空 ");
        }
        DeleteByQueryRequest request = new DeleteByQueryRequest(esDocDelVo.getIndexName());
        request.setQuery(QueryBuilders.matchQuery(esDocDelVo.getDelFileName(),esDocDelVo.getDelFileValue()));
        try {
            BulkByScrollResponse response = restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
            long deleted = response.getDeleted();
            if (deleted>0) {
                return R.ok(true, "删除成功！");
            }
        } catch (IOException e) {
            log.error("删除索引失败：{}", e);
            throw new RuntimeException("调用es服务删除索引失败，详情请查看日志");
        }
        return R.fail(false, "删除索引失败");
    }

    // 查询所有的索引方法
    @Override
    public List<String> selectAllIndexs() {
        List<String> list = new ArrayList<>();
        GetIndexRequest request = new GetIndexRequest("_all"); // "_all" 表示所有索引
        GetIndexResponse getIndexResponse = null;
        try {
            getIndexResponse = restHighLevelClient.indices().get(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 从响应中提取索引名列表
        for (String index : getIndexResponse.getIndices()) {
            list.add(index);
        }
        return list;
    }


}
